본문으로 건너뛰기

23.05.14

오늘 한 일

  • 알고리즘 문제 풀이
  • Nest.js 섹션 0 완료
  • 쿠버네티스 스터디 종료
  • 함수형 프로그래밍 스터디 6주차 공부 - 챕터 10,11 일급 함수 내용 정리

Nest.js 섹션 0 완료

Nest.js와 Express의 차이점

Nest.js는 Express 위에서 동작하는 프레임워크이다. Express의 기능을 모두 사용할 수 있고, Nest.js가 제공하는 기능을 사용할 수 있다.

Express는 라우터 위주의 설계라면, Nest.js는 모듈 위주의 설계이다.

Controller

  • res, req를 다루는 곳

  • 라우터 역할을 한다.

  • 라우터를 사용할 때는 @Get, @Post 등의 데코레이터를 사용한다.

    • 데코레이터 붙이면 Nest.js가 알아서 해준다. 개발자들이 어떻게 돌아가는지 몰라도 돌아가기 때문에 거부감을 느낄 수 있다. 이를 제어의 역전(IoC)이라고 한다.
  • module은 직접 구성해야한다는 점에서 스프링보다 IoC가 약하다.

  • module 파일에 만들어둔 controller를 추가해야한다.

Provider

  • Nest.js에서는 서비스를 provider라고 부른다.
  • 서비스를 제공하는 역할을 한다.
  • @Injectable() 데코레이터를 붙여서 사용한다.
// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}

// app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}

@Get()
getHello(): string {
return this.appService.getHello();
}
}

// app.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}

왜 서비스를 분리하지? 그냥 컨트롤러에 다 처리해서 리턴하면 되지 않나?

서비스의 역할은 비즈니스 로직의 분리이다. 컨트롤러는 요청을 받아서 응답을 해주는 역할만 하고, 비즈니스 로직은 서비스에서 처리한다.

이렇게 함으로써 서비스 단위에서는 req와 res에 대해서 모르고 딱 비즈니스 로직에만 집중할 수 있기 때문에 재사용하기 좋고 테스트 하기에도 좋다.

Express에서는 res.json() 형태로 리턴해주어야하는데, 이렇게 되면 테스트할 때 res를 모킹해서 해야하므로 번거로움이 있다. Nest.js에서는 서비스를 분리함으로써 이런 문제를 해결할 수 있다.

ConfigModule

dotenv 모듈 써도 되지만, 어차피 Nest의 모듈로 만들어서 사용해야하므로, Nest에서 제공하는 ConfigModule을 사용하는 것이 좋다.

// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
imports: [ConfigModule.forRoot()],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}

loggerMiddleware

Nest에서 로그를 찍을 때 console.log 대신 Logger 객체를 쓰는 것이 좋다. 여러 곳에서 log를 찍으면 어디서 찍은건지 알기 어렵기 때문이다.

Logger 객체를 사용하면 로그를 찍는 곳을 알 수 있다.

use() 메서드를 사용해서 미들웨어를 등록할 수 있다.

// app.module.ts
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerMiddleware } from './logger.middleware';

@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes('*');
}
}

// logger.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { Logger } from './logger.service';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
constructor(private readonly logger: Logger) {}

use(req: Request, res: Response, next: NextFunction) {
const { ip, method, originalUrl } = req;
const userAgent = req.get('user-agent') || '';

res.on('finish', () => {
const { statusCode } = res;
const contentLength = res.get('content-length');

this.logger.log(
`${method} ${originalUrl} ${statusCode} ${contentLength} - ${userAgent} ${ip}`
);
});

next();
}
}

implements, Dependency Injection(DI)

implements를 사용하면 무조건 해당 클래스에 있는 메서드를 구현해야한다. 강제성이 생기므로 좋다.

DI는 의존성 주입이다. 클래스에 의존성을 주입해주는 것이다. Nest에서는 @Injectable() 데코레이터를 사용해서 의존성을 주입한다.

DI를 해주면 직접 new를 사용해서 객체를 생성하지 않고 생성자에서 의존성을 주입받아서 사용할 수 있다.

  • 인스턴스를 안에서 만들어버리면 결합성이 강해지기 때문에 테스트하기 어렵다. 의존성을 주입받으면 추상과 구현이 분리되기 때문에 코드가 유연해지고 테스트하기도 쉽다.

내일 할 일

  • 알고리즘 문제 풀이
  • Nest.js 공식 문서 읽기
  • 익스텐션 개발(에러처리, UI 구현)
  • 포트폴리오 및 부스트캠프 자기소개서 작성